using System;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity.Interception;
using Microsoft.Practices.Unity.ObjectBuilder;

namespace Microsoft.Practices.Unity
{
    public class InterceptionExtension : UnityContainerExtension, IInterceptionConfiguration
    {
        protected override void Initialize()
        {
            Context.Locator.Add(typeof(IObjectFactory), new ObjectFactory(Context.Container));

            Context.Strategies.AddNew<InterceptionReflectionStrategy>(UnityBuildStage.PreCreation);
            Context.Strategies.AddNew<InterfaceInterceptionStrategy>(UnityBuildStage.PreCreation);
            Context.Strategies.AddNew<VirtualInterceptionStrategy>(UnityBuildStage.PreCreation);
            Context.Strategies.AddNew<RemotingInterceptionStrategy>(UnityBuildStage.PostInitialization);
        }

        void Intercept<TPolicy, TInterface>(Type typeToIntercept,
                                            MethodBase method,
                                            IInterceptionHandler[] handlers)
            where TInterface : IInterceptionPolicy
            where TPolicy : InterceptionPolicy, TInterface, new()
        {
            NamedTypeBuildKey key = new NamedTypeBuildKey(typeToIntercept);
            TPolicy policy = Context.Policies.Get<TInterface>(key, true) as TPolicy;

            if (policy == null)
            {
                policy = new TPolicy();
                Context.Policies.Set<TInterface>(policy, key);
            }

            policy.Add(method, handlers);
        }

        public void InterceptInterface<T>(MethodInfo interfaceMethod,
                                          params IInterceptionHandler[] handlers)
        {
            Intercept<InterfaceInterceptionPolicy, IInterfaceInterceptionPolicy>(typeof(T), interfaceMethod, handlers);
        }

        public void InterceptInterface(Type typeToIntercept,
                                       MethodInfo interfaceMethod,
                                       params IInterceptionHandler[] handlers)
        {
            Intercept<InterfaceInterceptionPolicy, IInterfaceInterceptionPolicy>(typeToIntercept, interfaceMethod, handlers);
        }

        public void InterceptRemoting<T>(MethodInfo method,
                                         params IInterceptionHandler[] handlers)
        {
            Intercept<RemotingInterceptionPolicy, IRemotingInterceptionPolicy>(typeof(T), method, handlers);
        }

        public void InterceptRemoting(Type typeToIntercept,
                                      MethodInfo method,
                                      params IInterceptionHandler[] handlers)
        {
            Intercept<RemotingInterceptionPolicy, IRemotingInterceptionPolicy>(typeToIntercept, method, handlers);
        }

        public void InterceptVirtual<T>(MethodInfo method,
                                        params IInterceptionHandler[] handlers)
        {
            Intercept<VirtualInterceptionPolicy, IVirtualInterceptionPolicy>(typeof(T), method, handlers);
        }

        public void InterceptVirtual(Type typeToIntercept,
                                     MethodInfo method,
                                     params IInterceptionHandler[] handlers)
        {
            Intercept<VirtualInterceptionPolicy, IVirtualInterceptionPolicy>(typeToIntercept, method, handlers);
        }
    }
}